package ecoll.client.centers;

import java.util.ArrayList;
import java.util.HashSet;

import com.google.gwt.appengine.channel.client.Channel;
import com.google.gwt.appengine.channel.client.ChannelError;
import com.google.gwt.appengine.channel.client.ChannelFactory;
import com.google.gwt.appengine.channel.client.Socket;
import com.google.gwt.appengine.channel.client.SocketListener;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

import ecoll.client.Client;
import ecoll.client.database.File;
import ecoll.client.quiz.QuizUserData;
import ecoll.client.widgets.QuizRankingPanel;
import ecoll.client.widgets.QuizRoomPanel;
import ecoll.client.widgets.QuizServerPanel;
import ecoll.client.widgets.QuizUserPanel;
import ecoll.shared.Constant;

public class VocabularyChallengeCenter implements Center {

	private QuizRoomPanel quizRoomPanel = new QuizRoomPanel();

	private Function createRoom = new Function() {
		@Override
		public void execute() {

			Client.DATABASE_SERVICE
					.getFiles(new AsyncCallback<ArrayList<File>>() {
						@Override
						public void onSuccess(ArrayList<File> files) {
							quizRoomPanel.clear();
							setRoomCreatingArea(files);
						}

						@Override
						public void onFailure(Throwable caught) {
							Window.alert("Failure fetching files!");
						}

					});

		}

		@Override
		public String name() {
			return "Create a new room";
		}

	};

	private Function joinRoom = new Function() {
		@Override
		public void execute() {

			VerticalPanel holder = new VerticalPanel();
			holder.setStyleName("fileUploadForm");

			final DialogBox dialogBox = new DialogBox();
			dialogBox.setWidget(holder);
			dialogBox.setText("Join a room");
			dialogBox.setStyleName("fileUploadDialogBox");

			final TextBox roomNameBox = new TextBox();
			roomNameBox.setText("Room name");
			roomNameBox.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					roomNameBox.setText("");
				}
			});
			holder.add(roomNameBox);
			holder.add(new HTML("<hr />"));

			DockPanel buttonPanel = new DockPanel();

			final Button joinButton = new Button("Join");
			buttonPanel.add(joinButton, DockPanel.WEST);

			joinButton.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					final String name = roomNameBox.getText();

					Client.QUIZ_SERVICE.joinRoom(name,
							new AsyncCallback<String>() {

								@Override
								public void onFailure(Throwable e) {
									Window.alert(e.getMessage());
								}

								@Override
								public void onSuccess(final String token) {
									if (token == null) {
										Window.alert("Connection failed!");
										return;
									}
									dialogBox.hide();
									openConnection(token, name);
								}
							});
				}
			});

			Button closeButton = new Button("Close");
			closeButton.addStyleName("button");
			buttonPanel.add(closeButton, DockPanel.EAST);

			closeButton.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					dialogBox.hide();
				}
			});

			holder.add(buttonPanel);

			dialogBox.center();
		}

		@Override
		public String name() {
			return "Join an existing room";
		}

	};

	@Override
	public String name() {
		return "Vocabulary Challenge";
	}

	@Override
	public void execute() {
	}

	@Override
	public Function function(int i) {
		if (i < 0 || i > FUNCTION.length)
			return null;
		return FUNCTION[i];
	}

	private final Function[] FUNCTION = new Function[] { createRoom, joinRoom };

	private void setRoomCreatingArea(ArrayList<File> files) {

		VerticalPanel fileList = new VerticalPanel();

		HorizontalPanel namePanel = new HorizontalPanel();
		namePanel.setStyleName("roomCreatingOptionsPanel");
		namePanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		HTML titleLabel = new HTML("<h3>Room name:</h3>");
		namePanel.add(titleLabel);
		final TextBox nameBox = new TextBox();
		nameBox.setStyleName("roomNameBox");
		namePanel.add(nameBox);
		Button createButton = new Button("Create");
		createButton.addStyleName("button");
		namePanel.add(createButton);
		fileList.add(namePanel);

		HorizontalPanel numberPanel = new HorizontalPanel();
		numberPanel.setStyleName("roomCreatingOptionsPanel");
		Label periodLabel = new Label("Time for each question (s): ");
		numberPanel.add(periodLabel);
		final ListBox periodBox = new ListBox();
		periodBox.addItem("5");
		periodBox.addItem("10");
		periodBox.addItem("20");
		periodBox.addItem("30");
		periodBox.addItem("60");
		periodBox.addItem("120");
		periodBox.setVisibleItemCount(1);
		periodBox.setItemSelected(4, true);
		periodBox.setStyleName("selectionBox");
		numberPanel.add(periodBox);

		Label delayLabel = new Label("Break between questions (s): ");
		numberPanel.add(delayLabel);
		final ListBox delayBox = new ListBox();
		delayBox.addItem("1");
		delayBox.addItem("2");
		delayBox.addItem("3");
		delayBox.addItem("5");
		delayBox.addItem("10");
		delayBox.addItem("20");
		delayBox.setVisibleItemCount(1);
		delayBox.setItemSelected(4, true);
		delayBox.setStyleName("selectionBox");
		numberPanel.add(delayBox);
		fileList.add(numberPanel);

		ScrollPanel fileScrollPanel = new ScrollPanel(fileList);
		fileList.setWidth("100%");
		final HashSet<Long> result = new HashSet<Long>();
		setFileListingArea(fileList, files, result);

		createButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				final String name = nameBox.getText();
				if (!name.matches("[a-zA-Z\\d_-]{1,20}")) {
					Window.alert("Room name must be up to 20 characters long "
							+ "and contain only leters, digits, \"_\" and \"-\"!");
					return;
				}
				if (result.size() == 0) {
					Window.alert("Choose at least one file to create the quiz!");
					return;
				}
				ArrayList<Long> files = new ArrayList<Long>(result);
				final int period = Integer.parseInt(periodBox
						.getValue(periodBox.getSelectedIndex()));
				final int delay = Integer.parseInt(delayBox.getValue(delayBox
						.getSelectedIndex()));

				Client.QUIZ_SERVICE.createRoom(name, period, delay, files,
						new AsyncCallback<String>() {

							@Override
							public void onFailure(Throwable e) {
								Window.alert(e.getMessage());
							}

							@Override
							public void onSuccess(String token) {
								if (token == null)
									Window.alert("Such room already exists! Choose a different name.");
								else
									openConnection(token, name);
							}
						});

			}
		});

		Client.updateDashboard(fileScrollPanel);
	}

	private void openConnection(String token, final String roomName) {

		ChannelFactory channelFactory = GWT.create(ChannelFactory.class);
		Channel channel = channelFactory.createChannel(token);
		Socket socket = channel.open(new SocketListener() {

			@Override
			public void onOpen() {
				setRoomArea(new VerticalPanel(), roomName);
				quizRoomPanel.addChatMessage("connected to " + roomName
						+ ", quiz loading...");

				displayQuizRoom(roomName);

			}

			@Override
			public void onMessage(String message) {
				/*
				 * if (!quizRoomPanel.isAttached()) {
				 * Window.alert("not attached"); displayQuizRoom(roomName); }
				 * else if (!quizRoomPanel.isVisible()) {
				 * Window.alert("not attached"); displayQuizRoom(roomName); }
				 * Window.alert(message);
				 */
				int idx = message.indexOf(" ");
				String typeString = message.substring(0, idx);
				Constant.Message type = Constant.Message.valueOf(typeString);
				String content = message.substring(idx + 1);
				switch (type) {
				case INFO:
					quizRoomPanel.addChatMessage(content);
					break;
				case QUESTION:
					content = typeString + ": " + content;
					quizRoomPanel.addServerTopMessage(content);
					break;
				case ANSWER:
					idx = content.indexOf(" ");
					String login = content.substring(0, idx);
					content = login + " got it right: "
							+ content.substring(idx + 1);
					quizRoomPanel.addUserScore(login);
					quizRoomPanel.addServerTopMessage(content);
					break;
				case NOANSWER:
					content = "Nobody answered correctly: " + content;
					quizRoomPanel.addServerTopMessage(content);
					break;
				case JOIN:
					quizRoomPanel.addUserRankData(content);
					content += " joined";
					quizRoomPanel.addChatMessage(content);
					break;
				case LEAVE:
					quizRoomPanel.removeUserRankData(content);
					content += " left";
					quizRoomPanel.addChatMessage(content);
					break;
				case COUNT:
					quizRoomPanel.addServerBottomMessage(content);
					break;
				}
			}

			@Override
			public void onError(ChannelError error) {
				Window.alert(error.getCode() + ": " + error.getDescription());
				new NewsCenter().execute();
			}

			@Override
			public void onClose() {
				leaveRoom(roomName);
			}
		});
		quizRoomPanel.setSocket(socket);
	}

	private void setRoomArea(VerticalPanel chatArea, final String roomName) {

		quizRoomPanel.setServerArea(new QuizServerPanel());
		quizRoomPanel.setChatArea(chatArea);
		quizRoomPanel.setUserArea(new QuizUserPanel(roomName));

	}

	private void setFileListingArea(VerticalPanel fileList,
			ArrayList<File> files, final HashSet<Long> result) {
		for (int i = 0; i < files.size(); ++i) {
			HorizontalPanel filePanel = new HorizontalPanel();
			if (i % 2 == 1)
				filePanel.setStyleName("evenLabel");
			else
				filePanel.setStyleName("oddLabel");

			final File file = files.get(i);

			CheckBox cb = new CheckBox();
			cb.setStyleName("fileCheckBox");
			cb.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(ClickEvent event) {
					boolean state = ((CheckBox) event.getSource()).getValue();
					if (state)
						result.add(file.getId());
					else
						result.remove(file.getId());

				}
			});
			filePanel.add(cb);

			DisclosurePanel fileInfo = new DisclosurePanel();

			Label fileName = new Label(file.getName());
			fileName.setStyleName("fileName");
			fileInfo.setHeader(fileName);

			HorizontalPanel hiddenInfo = new HorizontalPanel();
			Label label = new Label("    Size " + file.getSize() / 1024
					+ "kB.    Modified " + file.getDate() + ".    ");
			hiddenInfo.add(label);
			hiddenInfo.setCellVerticalAlignment(label,
					HasAlignment.ALIGN_MIDDLE);
			fileInfo.setContent(hiddenInfo);

			filePanel.add(fileInfo);
			fileInfo.setWidth("100%");
			filePanel.setCellWidth(fileInfo, "100%");

			fileList.add(filePanel);
		}
	}

	private void displayQuizRoom(final String roomName) {
		Client.updateDashboard(quizRoomPanel);

		Client.QUIZ_SERVICE.getQuestion(roomName, new AsyncCallback<String>() {
			@Override
			public void onFailure(Throwable e) {
				Window.alert(e.getMessage());
			}

			@Override
			public void onSuccess(String question) {
				if (question != null)
					quizRoomPanel.addServerTopMessage("QUESTION: " + question);
			}
		});
		Client.QUIZ_SERVICE.getRanking(roomName,
				new AsyncCallback<ArrayList<QuizUserData>>() {
					@Override
					public void onFailure(Throwable e) {
						Window.alert(e.getMessage());
					}

					@Override
					public void onSuccess(ArrayList<QuizUserData> ranking) {
						QuizRankingPanel rankingPanel = new QuizRankingPanel(
								roomName, ranking);
						quizRoomPanel.setRankingArea(rankingPanel);
					}
				});
	}

	private void leaveRoom(String roomName) {
		Client.QUIZ_SERVICE.leaveRoom(roomName, new AsyncCallback<Void>() {
			@Override
			public void onFailure(Throwable e) {
				Window.alert(e.getMessage());
			}

			@Override
			public void onSuccess(Void result) {
				Window.alert("Successfully disconnected!");
			}
		});
	}

}
